﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace FunctionalLayer {
    /// <summary>
    /// This class is used to calcualte the speed
    /// </summary>
    public class SpeedSensor {
        #region Fields
        public event Action NewSection;

        private int _currentSection;
        private double _sectionDistance;
        private int _previousAverageSpeed;
        private double _totalDistance;

        //The number of pulses received * the wheel circumference
        public double TotalDistance
        {
            get { return _totalDistance; }
            set { _totalDistance = value; }
        }

        /// <summary>
        /// Contains the time between the speed sensor pulses
        /// </summary>
        private List<double> _pulseInterupts;

        private int _listLength;
        #endregion

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="listLength">Length of the list</param>
        public SpeedSensor(int listLength)
        {
            _pulseInterupts = new List<double>();
            _listLength = listLength;
            _totalDistance = 0;
            _previousAverageSpeed = 0;

            //Calculate section distance
            if (ConfigSingleton.Settings.Laps != 0 && ConfigSingleton.Settings.SectionsPerLap != 0)
            {
                _sectionDistance = ConfigSingleton.Settings.LapDistance / (double)ConfigSingleton.Settings.SectionsPerLap;
            }

            _currentSection = 1;
        }

        /// <summary>
        /// This method needs to be called when the speed sensor gives a pulse
        /// </summary>
        /// <param name="milisBetween">The time between this pulse and the last pulse</param>
        public void NewPulse(double milisBetween)
        {
            _pulseInterupts.Insert(0, milisBetween);
            if (_pulseInterupts.Count >= _listLength)
            {
                _pulseInterupts.RemoveAt(_pulseInterupts.Count - 1);
            }

            //Update total distance and check new section
            _totalDistance += ConfigSingleton.Settings.WheelCircumference;
            if (_totalDistance - (_sectionDistance * _currentSection) >= 0)
            {
                _currentSection++;
                if (NewSection != null)
                {
                    NewSection();
                }
            }
        }

        /// <summary>
        /// Calculate the speed using the time between the pulses
        /// </summary>
        /// <returns>The avarage speed of the pulses</returns>
        public double CalculateSpeed()
        {
            double averageSpeed;

            double speedSum = 0;
            for (int i = 0; i < _pulseInterupts.Count; i++)
            {
                speedSum += ConfigSingleton.Settings.WheelCircumference * ((3600 * 1000) / _pulseInterupts[i]);
            }
            averageSpeed = speedSum / _pulseInterupts.Count;

            //Filter wrong input
            if (_previousAverageSpeed == 0)
            {
                _previousAverageSpeed = (int)averageSpeed;
                return averageSpeed;
            }
            if (_previousAverageSpeed <= 2 && averageSpeed >= _previousAverageSpeed * 10)
            {
                return _previousAverageSpeed;
            }
            else
            {
                _previousAverageSpeed = (int)averageSpeed;
                return averageSpeed;
            }
        }
    }
}